uv一つでPythonプロジェクトのランタイム・パッケージ管理が完結。pip/poetry/pyenv/virtualenvをまるっと置き換え!
Pythonのリンター・コード整形ツール Ruff を開発している Astral から、Pythonのパッケージツール uv の大型アップデート(uv v0.3)の知らせが届きました。
発表されたブログには「uv: Unified Python packaging(訳:統一されたPythonパッケージング)」とあり、READMEには以下の一文があります。
A single tool to replace pip, pip-tools, pipx, poetry, pyenv, virtualenv, and more.
プロジェクトのランタイムのバージョン管理(pyenv/.python-version)、プロジェクト固有の環境(virtualenv)、パッケージ管理(pip/poetry/pyproject.toml)など、これまでは守備範囲が少しずつ異なるツール群を補完して使い分ける必要がありました。uv を使えばこれらを一つに集約できるようになり、調べるたびに異なる情報が見つかるパッケージ管理・初期セットアップの混迷がきれいに整理されるようになることが期待されます。
また、Rust製であることと後発の強みか、非常に高速です。
※ 公式ブログから
Python向けリンター・コード整形ツール Ruff は Flake8/Black/isort など周辺エコシステム全体を1ツールに集約できるように発展しました。それと同じことが、uv のおかげでパッケージ関連界隈でも起こっています。
また、Cargo風のワークスペース、ツール実行、スクリプト内のインラインメタデータ管理などの機能も提供されています。
uv のインストール
uvはインストーラーが提供されています。
~$ curl -LsSf https://astral.sh/uv/install.sh | sh
downloading uv 0.3.1 x86_64-unknown-linux-gnu
installing to /home/ubuntu/.cargo/bin
uv
uvx
everything's installed!
To add $HOME/.cargo/bin to your PATH, either restart your shell or run:
source $HOME/.cargo/env (sh, bash, zsh)
source $HOME/.cargo/env.fish (fish)
pip
からもインストールできます
$ pip install uv
uv を使ったランタイム・パッケージ管理の始め方
uv
のパッケージ管理は、poetry
(rye
)/pyproject.toml
を使った運用とほぼ同じです。
~/test$ uv init && uv add "requests>=2.32"
Initialized project `test`
Using Python 3.10.0Creating virtualenv at: .venv
Resolved 6 packages in 25ms
Built test @ file:///home/ubuntu/test
Prepared 6 packages in 566ms
Installed 6 packages in 3ms
+ certifi==2024.7.4
+ charset-normalizer==3.3.2
+ idna==3.7
+ requests==2.32.3
+ test==0.1.0 (from file:///home/ubuntu/test)
+ urllib3==2.2.2
~/test$ tree .
.
├── README.md
├── pyproject.toml
├── src
│ └── test
│ └── __init__.py
└── uv.lock
3 directories, 4 files
~/test$ cat pyproject.toml
[project]
name = "test"
version = "0.1.0"
description = "Add your description here"
readme = "README.md"
requires-python = ">=3.10"
dependencies = [
"requests>=2.32",
]
[build-system]
requires = ["hatchling"]
build-backend = "hatchling.build"
~/test$ head uv.lock
version = 1
requires-python = ">=3.10"
[[package]]
name = "certifi"
version = "2024.7.4"
source = { registry = "https://pypi.org/simple" }
sdist = { url = "https://files.pythonhosted.org/packages/c2/02/a95f2b11e207f68bc64d7aae9666fed2e2b3f307748d5123dffb72a1bbea/certifi-2024.7.4.tar.gz", has
h = "sha256:5a1e7645bc0ec61a09e26c36f6106dd4cf40c6db3a1fb6352b0244e7fb057c7b", size = 164065 }
wheels = [
{ url = "https://files.pythonhosted.org/packages/1c/d5/c84e1a17bf61d4df64ca866a1c9a913874b4e9bdc131ec689a0ad013fb36/certifi-2024.7.4-py3-none-any.whl
", hash = "sha256:c198e21b1289c2ab85ee4e67bb4b4ef3ead0892059901a8d5b622f24a1101e90", size = 162960 },
Pythonのバージョンを固定(ピン留め)することもできます。
~/test$ uv python pin 3.10
Pinned `.python-version` to `3.10`
~/test$ cat .python-version
3.10
プロジェクトごとに .venv
という Virtual Env環境が自動的(透過)に作成されます。
以下の様なPythonランタイム情報を表示するプログラムを用意します。
import sys
print(sys.version)
print(sys.executable)
システムPythonから呼び出すと、そのバージョンが表示されます。
~/test$$ python3 foo.py
3.12.3 (main, Apr 10 2024, 05:33:47) [GCC 13.2.0]
/usr/bin/python3
プロジェクト内から uv run
で実行すると、このプロジェクト固有のPythonが呼び出されます。
~/test$ uv run foo.py
3.10.0 (default, Oct 18 2021, 02:11:22) [Clang 13.0.0 ]
/home/ubuntu/test/.venv/bin/python3
ユーザーは virtual env を意識する必要はありません。
pyenv の代わりに Python ランタイム管理
uv
には pyenv
相当のPythonバージョン切り替え機能も含まれています。
$uv python list
でインストール可能なバージョン一覧・インストール状況を確認し、$ uv python install バージョン
でインストールします。
$ uv python list
cpython-3.12.5-linux-x86_64-gnu <download available>
cpython-3.12.3-linux-x86_64-gnu /usr/bin/python3.12
cpython-3.12.3-linux-x86_64-gnu /usr/bin/python3 -> python3.12
cpython-3.12.3-linux-x86_64-gnu /bin/python3.12
cpython-3.12.3-linux-x86_64-gnu /bin/python3 -> python3.12
cpython-3.11.9-linux-x86_64-gnu <download available>
cpython-3.10.14-linux-x86_64-gnu <download available>
cpython-3.10.0-linux-x86_64-gnu /home/ubuntu/.local/share/uv/python/cpython-3.10.0-linux-x86_64-gnu/bin/python3 -> python3.10
cpython-3.9.19-linux-x86_64-gnu <download available>
cpython-3.8.19-linux-x86_64-gnu <download available>
pypy-3.7.13-linux-x86_64-gnu <download available>
$ uv python install 3.11
Searching for Python versions matching: Python 3.11
Installed Python 3.11.9 in 2.90s
+ cpython-3.11.9-linux-x86_64-gnu
~/test$ uv python list
cpython-3.12.5-linux-x86_64-gnu <download available>
cpython-3.12.3-linux-x86_64-gnu /usr/bin/python3.12
cpython-3.12.3-linux-x86_64-gnu /usr/bin/python3 -> python3.12
cpython-3.12.3-linux-x86_64-gnu /bin/python3.12
cpython-3.12.3-linux-x86_64-gnu /bin/python3 -> python3.12
cpython-3.11.9-linux-x86_64-gnu /home/ubuntu/.local/share/uv/python/cpython-3.11.9-linux-x86_64-gnu/bin/python3 -> python3.11
cpython-3.10.14-linux-x86_64-gnu <download available>
cpython-3.10.0-linux-x86_64-gnu /home/ubuntu/.local/share/uv/python/cpython-3.10.0-linux-x86_64-gnu/bin/python3 -> python3.10
cpython-3.9.19-linux-x86_64-gnu <download available>
cpython-3.8.19-linux-x86_64-gnu <download available>
pypy-3.7.13-linux-x86_64-gnu <download available>
Python Standalone BuildsのPythonがインストールされます。
ツール管理
Pythonで書かれたコマンドラインツールを隔離環境とは別にインストールして実行したい時があります。
pipx がそのような用途に使われましたが、uv
も可能です。uvx
に続けて、プログラム名とサブコマンドを指定するだけで、依存ライブラリをインストールしてCLIプログラムを実行できます。
$ uvx ruff check
$ uvx tox
なお、uvx
は uv tool run
のエイリアスです。
$ uv tool run ruff check
ツールとしてインストールすることも可能です
$ uv tool install ruff
Resolved 1 package in 29ms
Installed 1 package in 3ms
+ ruff==0.6.1
Installed 1 executable: ruff
warning: `/home/ubuntu/.local/bin` is not on your PATH. To use installed tools, run `export PATH="/home/ubuntu/.local/bin:$PATH"` or `uv tool update-shell`.
$ uv tool dir
/home/ubuntu/.local/share/uv/tools
$ ls /home/ubuntu/.local/share/uv/tools
ruff
$ uv tool upgrade ruff
Nothing to upgrade
バイナリは /home/ubuntu/.local/bin
以下にインストールされるので、パスを通しましょう。
PEP723:インラインスクリプトメタデータの依存関係解決にも対応
次のような requests
モジュールを使ったPythonスクリプトを用意します。
import requests
print(requests.get("https://astral.sh").status_code)
初期状態では requests
モジュールが存在しないため、実行時にインポートエラーになります。
$ uv run example.py
Traceback (most recent call last):
File "/home/ubuntu/pep723/example.py", line 1, in <module>
import requests; print(requests.get("https://astral.sh").status_code)
^^^^^^^^^^^^^^^
ModuleNotFoundError: No module named 'requests'
PEP 723 – Inline script metadata の規格により、このスクリプトの依存情報をスクリプト内にメタ情報として埋め込めます。
uv
は、この埋込操作に対応しています。
$ uv add --script example.py requests
Updated `example.py`
$ cat example.py
# /// script
# requires-python = ">=3.11"
# dependencies = [
# "requests",
# ]
# ///
import requests; print(requests.get("https://astral.sh").status_code)
もう一度実行してみましょう。
$ uv run example.py
Reading inline script metadata from: example.py
200
インライン記述されたメタ情報からライブラリの依存関係を解決しています。
最後に
PythonプロジェクトのオールインワンなPythonランタイム・パッケージ管理ツール uv について紹介しました。
uv
は2024年の初頭にリリースされた新しいツールであり、つい最近までは Rye というツール(Flaskの作者のArmin Ronacher/@mitsuhikoが作成)に多くの機能を依存していましたが、uv
は Rye
の機能を置き換える(依存しない)ように開発されています。
今回の uv 0.3 のリリースに伴うRye原作者のArmin Ronacherのブログを拝見すると、Pythonのパッケージ管理周りは変化が早く、選択肢も多すぎてうんざりしており、開発者、特に新参者が困惑しないように統一されることを望んでおり、uv への移行検討をすすめています。
If you are using Rye today, consider this blog post as a reminder that you should probably starting having a closer look at uv and give feedback to the Astral folks.
...
there is so much choice, so many tools that are not quite compatible, and by the inconsistency everywhere. I have seen people walk down one tool, just to re-emerge moving their entire stack to conda and back because they hit some wall.
...
If you maintain an important Python project I would ask you to give uv a try and ask yourself if you would consider pointing people towards it. I think that this is our best shot in the community at finding ourselves in a much better position than we have ever been.
まとまった規模の既存プロジェクトで、特に pyproject.toml
のPEP外拡張を多様しているケースでは、uv
へのいきなりの移行は少し敷居が高いかもしれませんが、リスクの取れる新規・小さめのプロジェクトでは、評価してみてもよいかもしれません。